home *** CD-ROM | disk | FTP | other *** search
/ Internet Info 1994 March / Internet Info CD-ROM (Walnut Creek) (March 1994).iso / networking / ip / ka9q / alpha.arc / SMTPCLI.C < prev    next >
C/C++ Source or Header  |  1988-06-06  |  22KB  |  989 lines

  1.  
  2. /*
  3.  *    Client routines for Simple Mail Transfer Protocol ala RFC821
  4.  *    A.D. Barksdale Garbee II, aka Bdale, N3EUA
  5.  *    Copyright 1986 Bdale Garbee, All Rights Reserved.
  6.  *    Permission granted for non-commercial copying and use, provided
  7.  *    this notice is retained.
  8.  *     Modified 14 June 1987 by P. Karn for symbolic target addresses,
  9.  *    also rebuilt locking mechanism
  10.  *    Limit on max simultaneous sessions, reuse of connections - 12/87 NN2Z
  11.  *    Added return of mail to sender as well as batching of commands 1/88 nn2z
  12.  */
  13. #include <stdio.h>
  14. #include <fcntl.h>
  15. #include <time.h>
  16. #ifdef UNIX
  17. #include <sys/types.h>
  18. #endif
  19. #include "global.h"
  20. #include "netuser.h"
  21. #include "mbuf.h"
  22. #include "timer.h"
  23. #include "tcp.h"
  24. #include "smtp.h"
  25. #include "trace.h"
  26. #include "cmdparse.h"
  27.  
  28. extern int16 lport;            /* local port placeholder */
  29. extern int32 resolve();
  30. static struct timer smtpcli_t;
  31. int32 gateway;
  32.  
  33. #ifdef SMTPTRACE
  34. int16    smtptrace = 0;            /* used for trace level */
  35. int dosmtptrace();
  36. #endif
  37.  
  38. int16    smtpmaxcli  = MAXSESSIONS;    /* the max client connections allowed */
  39. int16    smtpsessions = 0;        /* number of client connections
  40.                     * currently open */
  41. int16    smtpmode = 0;
  42.  
  43. static struct smtp_cb *cli_session[MAXSESSIONS]; /* queue of client sessions  */
  44.  
  45. static char quitcmd[] = "QUIT\r\n";
  46. static char eom[] = "\r\n.\r\n";
  47.  
  48. int smtptick(),dogateway(),dosmtpmaxcli(),mlock(),dotimer(),nextjob();
  49. int setsmtpmode(),sendwindow();
  50. void quit(),abort_trans(),retmail(),sendit(),del_session(),del_job();
  51. void execjobs(),smtp_transaction(),logerr();
  52. struct smtp_cb *newcb(),*lookup();
  53. struct smtp_job *setupjob();
  54.  
  55. struct cmds smtpcmds[] = {
  56.     "gateway",    dogateway,    0,    NULLCHAR,    NULLCHAR,
  57.     "mode",        setsmtpmode,    0,    NULLCHAR,    NULLCHAR,
  58.     "kick",        smtptick,    0,    NULLCHAR,    NULLCHAR,
  59.     "maxclients",    dosmtpmaxcli,    0,    NULLCHAR,    NULLCHAR,
  60.     "timer",    dotimer,    0,    NULLCHAR,    NULLCHAR,
  61. #ifdef SMTPTRACE
  62.     "trace",    dosmtptrace,    0,    NULLCHAR,    NULLCHAR,
  63. #endif
  64.     NULLCHAR,    NULLFP,        0,    
  65.     "subcommands: gateway kick maxclients timer trace",
  66.         NULLCHAR,
  67. };
  68.  
  69. dosmtp(argc,argv)
  70. int argc;
  71. char *argv[];
  72. {
  73.     return subcmd(smtpcmds,argc,argv);
  74. }
  75.  
  76. static int
  77. dosmtpmaxcli(argc,argv)
  78. int argc;
  79. char *argv[];
  80. {
  81.     int x;
  82.     if (argc < 2)
  83.         printf("%d\n",smtpmaxcli);
  84.     else {
  85.         x = atoi(argv[1]);
  86.         if (x > MAXSESSIONS)
  87.             printf("max clients must be <= %d\n",MAXSESSIONS);
  88.         else
  89.             smtpmaxcli = x;
  90.     }
  91.     return 0;
  92. }
  93.  
  94. static int
  95. setsmtpmode(argc,argv)
  96. int argc;
  97. char *argv[];
  98. {
  99.     if (argc < 2) {
  100.         printf("smtp mode: %s\n",
  101.             (smtpmode & QUEUE) ? "queue" : "route");
  102.     } else {
  103.         switch(*argv[1]) {
  104.         case 'q':
  105.             smtpmode |= QUEUE;
  106.             break;
  107.         case 'r':
  108.             smtpmode &= ~QUEUE;
  109.             break;
  110.         default:
  111.             printf("Usage: smtp mode [queue | route]\n");
  112.             break;
  113.         }
  114.     }
  115.     return 0;
  116. }
  117. static int
  118. dogateway(argc,argv)
  119. int argc;
  120. char *argv[];
  121. {
  122.     char *inet_ntoa();
  123.     int32 n;
  124.     extern char badhost[];
  125.  
  126.     if(argc < 2){
  127.         printf("%s\n",inet_ntoa(gateway));
  128.     } else if((n = resolve(argv[1])) == 0){
  129.         printf(badhost,argv[1]);
  130.         return 1;
  131.     } else
  132.         gateway = n;
  133.     return 0;
  134. }
  135.  
  136. #ifdef SMTPTRACE
  137. static int
  138. dosmtptrace(argc,argv)
  139. int argc;
  140. char *argv[];
  141. {
  142.     if (argc < 2)
  143.         printf("%d\n",smtptrace);
  144.     else 
  145.         smtptrace = atoi(argv[1]);
  146.     return 0;
  147. }
  148. #endif
  149.  
  150. /* Set outbound spool poll interval */
  151. static int
  152. dotimer(argc,argv)
  153. int argc;
  154. char *argv[];
  155. {
  156.     int smtptick();
  157.  
  158.     if(argc < 2){
  159.         printf("%lu/%lu\n",
  160.         (smtpcli_t.start - smtpcli_t.count)/(1000/MSPTICK),
  161.         smtpcli_t.start/(1000/MSPTICK));
  162.         return 0;
  163.     }
  164.     smtpcli_t.func = (void (*)())smtptick;/* what to call on timeout */
  165.     smtpcli_t.arg = NULLCHAR;        /* dummy value */
  166.     smtpcli_t.start = atoi(argv[1])*(1000/MSPTICK);    /* set timer duration */
  167.     start_timer(&smtpcli_t);        /* and fire it up */
  168.     return 0;
  169. }
  170.  
  171. /* this is the routine that gets called every so often to do outgoing mail
  172.    processing */
  173. int
  174. smtptick()
  175. {
  176.     register struct smtp_cb *cb;
  177.     struct smtp_job *jp;
  178.     struct list *ap;
  179.     char    tmpstring[LINELEN], wfilename[13], prefix[9];
  180.     char    from[LINELEN], to[LINELEN];
  181.     char *cp, *cp1;
  182.     int32 destaddr;
  183.     FILE *wfile;
  184.  
  185. #ifdef SMTPTRACE
  186.     if (smtptrace > 5) {
  187.         printf("smtp daemon entered\n");
  188.         fflush(stdout);
  189.     }
  190. #endif
  191.     for(filedir(mailqueue,0,wfilename);wfilename[0] != '\0';
  192.         filedir(mailqueue,1,wfilename)){
  193.  
  194.         /* save the prefix of the file name which it job id */
  195.         cp = wfilename;
  196.         cp1 = prefix;
  197.         while (*cp && *cp != '.')
  198.             *cp1++ = *cp++;
  199.         *cp1 = '\0';
  200.  
  201.         /* lock this file from the smtp daemon */
  202.         if (mlock(mailqdir,prefix))
  203.             continue;
  204.  
  205.         sprintf(tmpstring,"%s/%s",mailqdir,wfilename);
  206.         if ((wfile = fopen(tmpstring,"r")) == NULLFILE) {
  207.             /* probably too many open files */
  208.             (void) rmlock(mailqdir,prefix);
  209.             /* continue to next message. The failure
  210.             * may be temporary */
  211.             continue;
  212.         }
  213.  
  214.         (void) fgets(tmpstring,LINELEN,wfile);    /* read target host */
  215.         rip(tmpstring);
  216.  
  217.         if ((destaddr = mailroute(tmpstring)) == 0) {
  218.             fclose(wfile);
  219.             printf("** smtp: Unknown address %s\n",tmpstring);
  220.             fflush(stdout);
  221.             (void) rmlock(mailqdir,prefix);
  222.             continue;
  223.         }
  224.  
  225.         if ((cb = lookup(destaddr)) == NULLCB) {
  226.             /* there are enough processes running already */
  227.             if (smtpsessions >= smtpmaxcli) {
  228. #ifdef SMTPTRACE
  229.                 if (smtptrace) {
  230.                     printf("smtp daemon: too many processes\n");
  231.                     fflush(stdout);
  232.                 }
  233. #endif
  234.                 fclose(wfile);
  235.                 (void) rmlock(mailqdir,prefix);
  236.                 break;
  237.             }
  238.             if ((cb = newcb()) == NULLCB) {
  239.                 fclose(wfile);
  240.                 (void) rmlock(mailqdir,prefix);
  241.                 break;
  242.             } 
  243.             cb->ipdest = destaddr;
  244.         } else {
  245.             /* This system is already is sending mail lets not
  246.             * interfere with its send queue.
  247.             */
  248.             if (cb->state != CLI_INIT_STATE) {
  249.                 fclose(wfile);
  250.                 (void) rmlock(mailqdir,prefix);
  251.                 continue;
  252.             }
  253.         }
  254.  
  255.         (void) fgets(from,LINELEN,wfile);    /* read from */
  256.         rip(from);
  257.         if ((jp = setupjob(cb,prefix,from)) == NULLJOB) {
  258.             fclose(wfile);
  259.             (void) rmlock(mailqdir,prefix);
  260.             del_session(cb);
  261.             break;
  262.         }
  263.         while (fgets(to,LINELEN,wfile) != NULLCHAR) {
  264.             rip(to);
  265.             if (addlist(&jp->to,to,DOMAIN) == NULLLIST) {
  266.                 fclose(wfile);
  267.                 del_session(cb);
  268.             }
  269.         }
  270.         fclose(wfile);
  271. #ifdef SMTPTRACE
  272.         if (smtptrace > 1) {
  273.             printf("queue job %s From: %s To:",prefix,from);
  274.             for (ap = jp->to; ap != NULLLIST; ap = ap->next)
  275.                 printf(" %s",ap->val);
  276.             printf("\n");
  277.             fflush(stdout);
  278.         }
  279. #endif
  280.     }
  281.  
  282.     /* start sending that mail */
  283.     execjobs();
  284.  
  285.     /* Restart timer */
  286.     start_timer(&smtpcli_t);
  287. }
  288.  
  289. /* this is the master state machine that handles a single SMTP transaction */
  290. void
  291. smtp_transaction(cb)
  292. register struct smtp_cb *cb;
  293. {
  294.     void smtp_cts();
  295.     register char reply;
  296.     register struct list *tp;
  297.     int16 cnt;
  298.     struct mbuf *bp,*bpl;
  299.     char tbuf[LINELEN];
  300.     int rcode;
  301.  
  302. #ifdef SMTPTRACE
  303.     if (smtptrace > 5) 
  304.         printf("smtp_transaction() enter state=%u\n",cb->state);
  305.     if (smtptrace) {
  306.         printf("%s\n",cb->buf);
  307.         fflush(stdout);
  308.     }
  309. #endif
  310.     /* Another line follows; ignore this one */
  311.     if(cb->buf[0] == '0' || cb->buf[3] == '-')
  312.         return;
  313.  
  314.     reply = cb->buf[0];
  315.     rcode = atoi(cb->buf);
  316.  
  317.     /* if service shuting down */
  318.     if (rcode == 421) {
  319.         quit(cb);
  320.         return;
  321.     }
  322.  
  323.     switch(cb->state) {
  324.     case CLI_OPEN_STATE:
  325.         if (reply != '2')
  326.             quit(cb);
  327.         else {
  328.             cb->state = CLI_HELO_STATE;
  329.             sendit(cb,"HELO %s\r\nMAIL FROM:<%s>\r\n",
  330.             hostname,cb->jobq->from);
  331.         }
  332.         break;
  333.     case CLI_HELO_STATE:
  334.         if (reply != '2')
  335.             quit(cb);
  336.         else 
  337.             cb->state = CLI_MAIL_STATE;
  338.         break;            
  339.     case CLI_MAIL_STATE:
  340.         if (reply != '2')
  341.             quit(cb);
  342.         else {
  343.             cb->state = CLI_RCPT_STATE;
  344.             cb->rcpts = 0;
  345.             bpl = NULLBUF;
  346.             for (tp = cb->jobq->to; tp != NULLLIST; tp = tp->next){
  347.                 sprintf(tbuf,"RCPT TO:<%s>\r\n",tp->val);
  348.                 bp = qdata(tbuf,(int16)strlen(tbuf));
  349.                 if (bp == NULLBUF) {
  350.                     free_p(bpl);
  351.                     quit(cb);
  352.                     return;
  353.                 }
  354.                 append(&bpl,bp);
  355.                 cb->rcpts++;
  356. #ifdef SMTPTRACE
  357.                 if (smtptrace) {
  358.                     printf(">>> %s",tbuf);
  359.                     fflush(stdout);
  360.                 }
  361. #endif
  362.             }
  363.             send_tcp(cb->tcb,bpl);
  364.         }
  365.         break;
  366.     case CLI_RCPT_STATE:
  367.         if (reply == '5') {
  368.             logerr(cb);
  369.         } else if (reply == '2') {
  370.             cb->goodrcpt =1;
  371.         } else {
  372.             /* some kind of temporary failure */
  373.             abort_trans(cb);
  374.             break;
  375.         }
  376.         /* if more rcpts stay in this state */
  377.         if (--cb->rcpts != 0)
  378.             break;
  379.  
  380.         /* check for no good rcpt on the list */
  381.         if (cb->goodrcpt == 0) {
  382.             if (cb->errlog != NULLLIST)
  383.                 retmail(cb);
  384.             (void) unlink(cb->wname);    /* unlink workfile */
  385.             (void) unlink(cb->tname);    /* unlink text */
  386.             abort_trans(cb);
  387.             break;
  388.         }
  389.         /* if this file open fails abort */
  390.         if ((cb->tfile = fopen(cb->tname,"r")) == NULLFILE)
  391.             abort_trans(cb);
  392.         else {
  393.             /* optimize for slow packet links by sending
  394.              * DATA cmd and the 1st window of text
  395.              */
  396.             if (cb->tcb->window <= cb->tcb->sndcnt)
  397.                 cnt = 0;
  398.             else
  399.                 cnt = cb->tcb->window - cb->tcb->sndcnt;
  400.             bp = qdata("DATA\r\n",6);
  401.             cb->cts = 1;
  402.             cb->state = CLI_SEND_STATE;
  403.             if (sendwindow(cb,bp,cnt) == EOF)
  404.                 cb->cts = 0;
  405.         }
  406.         break;
  407.     case CLI_SEND_STATE:
  408.         if (reply == '3') {
  409.             cb->state = CLI_UNLK_STATE;
  410.         } else {
  411.             /* change cts to transmit upcall queueing more data */
  412.             cb->cts = 0;
  413.             quit(cb);
  414.         }
  415.         break;
  416.     case CLI_UNLK_STATE:
  417.         /* if a good transfer or permanent failure remove job */
  418.         if (reply == '2' || reply == '5') {
  419.             if (reply == '5')
  420.                 logerr(cb);
  421.             /* close and unlink the textfile */
  422.             if(cb->tfile != NULLFILE) {
  423.                 fclose(cb->tfile);
  424.                 cb->tfile = NULLFILE;
  425.             }
  426.             if (cb->errlog != NULLLIST)
  427.                 retmail(cb);
  428.             (void) unlink(cb->tname);
  429.             (void) unlink(cb->wname);    /* unlink workfile */
  430.             log(cb->tcb,"SMTP sent job %s To: %s From: %s",
  431.             cb->jobq->jobname,cb->jobq->to->val,cb->jobq->from);
  432.         }
  433.         if (nextjob(cb)) {
  434.             cb->state = CLI_MAIL_STATE;
  435.             sendit(cb,"MAIL FROM:<%s>\r\n",cb->jobq->from);
  436.         } else 
  437.             /* the quit sent already in smtp_cts */
  438.             cb->state = CLI_QUIT_STATE;
  439.         break;
  440.     case CLI_IDLE_STATE:    /* used after a RSET and more mail to send */
  441.         if (reply != '2')
  442.             quit(cb);
  443.         else {
  444.             cb->state = CLI_MAIL_STATE;
  445.             sendit(cb,"MAIL FROM:<%s>\r\n",cb->jobq->from);
  446.         }
  447.         break;            
  448.     case CLI_QUIT_STATE:
  449.         break;
  450.     }
  451. }
  452.  
  453. /* abort the currrent job.
  454.  * If more work exists set up the next job if
  455.  * not then shut down.
  456. */
  457. static void
  458. abort_trans(cb)
  459. register struct smtp_cb *cb;
  460. {
  461.     if(cb->tfile != NULLFILE) {
  462.         fclose(cb->tfile);
  463.         cb->tfile = NULLFILE;
  464.     }
  465.     if (nextjob(cb)) {
  466.         sendit(cb,"RSET\r\n");
  467.         cb->state = CLI_IDLE_STATE;
  468.     } else 
  469.         quit(cb);
  470. }
  471.  
  472. /* close down link after a failure */
  473. static void
  474. quit(cb)
  475. struct smtp_cb *cb;
  476. {
  477.     cb->state = CLI_QUIT_STATE;
  478.     sendit(cb,quitcmd);        /* issue a quit command */
  479.     close_tcp(cb->tcb);        /* close up connection */
  480. }
  481.  
  482. /* smtp receiver upcall routine.  fires up the state machine to parse input */
  483. static
  484. void
  485. smtp_rec(tcb,cnt)
  486. struct tcb *tcb;
  487. int16 cnt;
  488. {
  489.     register struct smtp_cb *cb;
  490.     char *inet_ntoa();
  491.     char c;
  492.     struct mbuf *bp;
  493.  
  494. #ifdef SMTPTRACE
  495.     if (smtptrace > 7)  {
  496.         printf("smtp_rec called\n");
  497.         fflush(stdout);
  498.     }
  499. #endif
  500.     cb = (struct smtp_cb *)tcb->user;    /* point to our struct */
  501.     recv_tcp(tcb,&bp,cnt);    /* suck up chars from low level routine */
  502.  
  503.     /* Assemble input line in buffer, return if incomplete */
  504.     while(pullup(&bp,&c,1) == 1) {
  505.         switch(c) {
  506.         case '\r':    /* strip cr's */
  507.             continue;
  508.         case '\n':    /* line is finished, go do it! */
  509.             cb->buf[cb->cnt] = '\0';
  510.             smtp_transaction(cb);
  511.             cb->cnt = 0;
  512.             break;
  513.         default:    /* other chars get added to buffer */
  514.             if(cb->cnt != LINELEN-1)
  515.                 cb->buf[cb->cnt++] = c;
  516.             break;
  517.         }
  518.     }
  519. }
  520.  
  521. /* smtp transmitter ready upcall routine.  twiddles cts flag */
  522. static 
  523. void
  524. smtp_cts(tcb,cnt)
  525. struct tcb *tcb;
  526. int16 cnt;
  527. {
  528.     register struct smtp_cb *cb;
  529.  
  530. #ifdef SMTPTRACE
  531.     if (smtptrace > 7) {
  532.         printf("smtp_cts called avail %d\n",cnt);
  533.         fflush(stdout);
  534.     }
  535. #endif
  536.     cb = (struct smtp_cb *)tcb->user;    /* point to our struct */
  537.  
  538.     /* don't do anything until/unless we're supposed to be sending */
  539.     if(cb->cts == 0)
  540.         return;
  541.  
  542.     if (sendwindow(cb,NULLBUF,cnt) == EOF)
  543.         cb->cts = 0;
  544. }
  545.  
  546. /* fill the rest of the window with data and send out the eof commands.
  547. * It is done this way to minimize the number of packets sent.
  548. */
  549. static int
  550. sendwindow(cb,ibp,cnt)
  551. register struct smtp_cb *cb;
  552. struct mbuf *ibp;
  553. int16 cnt;
  554. {
  555.     struct mbuf *bpl;
  556.     register struct mbuf *bp;
  557.     char *cp;
  558.     int c;
  559.     
  560.     bpl = ibp;
  561.     if((bp = alloc_mbuf(cnt)) == NULLBUF){
  562.         /* Hard to know what to do here */
  563.         return EOF;
  564.     }
  565.     cp = bp->data;
  566.     while(cnt > 1 && (c = getc(cb->tfile)) != EOF){
  567. #if (defined(UNIX) || defined(MAC) || defined(AMIGAa))
  568.         if(c == '\n'){
  569.             *cp++ = '\r';
  570.             bp->cnt++;
  571.             cnt--;
  572.         }
  573. #endif
  574.         *cp++ = c;
  575.         bp->cnt++;
  576.         cnt--;
  577.     }
  578.     append(&bpl,bp);
  579.     if (cnt > 1) {    /* EOF seen */
  580.         fclose(cb->tfile);
  581.         cb->tfile = NULLFILE;
  582.         /* send the end of data character. */
  583.         if (cnt < sizeof(eom) - 1) {
  584.             bp = qdata(eom,5);
  585.             append(&bpl,bp);
  586.             cnt = 0;    /* dont let anyone else in */
  587.         } else {
  588.             memcpy(&bp->data[bp->cnt],eom,sizeof(eom) - 1);
  589.             bp->cnt += sizeof(eom) - 1;
  590.             cnt -= sizeof(eom) - 1;
  591.         }
  592.         /* send the quit in this packet if last job */
  593.         if (cb->jobq->next == NULLJOB) {
  594.             if (cnt < sizeof(quitcmd) - 1) {
  595.                 bp = qdata(quitcmd,sizeof(quitcmd) - 1);
  596.                 append(&bpl,bp);
  597.             } else {
  598.                 memcpy(&bp->data[bp->cnt],
  599.                 quitcmd,sizeof(quitcmd) - 1);
  600.                 bp->cnt += sizeof(quitcmd) - 1;
  601.             }
  602.         }
  603.         send_tcp(cb->tcb,bpl);
  604.         if (cb->jobq->next == NULLJOB)
  605.             close_tcp(cb->tcb);    /* close up connection */
  606.         return EOF;
  607.     } else {
  608.         send_tcp(cb->tcb,bpl);
  609.         return 0;
  610.     }
  611. }
  612.  
  613. /* smtp state change upcall routine. */
  614. static
  615. void
  616. smtp_state(tcb,old,new)
  617. register struct tcb *tcb;
  618. char old,new;
  619. {
  620.     register struct smtp_cb *cb;
  621.     extern char *tcpstates[];
  622.  
  623. #ifdef SMTPTRACE
  624.     if (smtptrace > 7) {
  625.         printf("smtp_state called: %s\n",tcpstates[new]);
  626.         fflush(stdout);
  627.     }
  628. #endif
  629.     cb = (struct smtp_cb *)tcb->user;
  630.     switch(new) {
  631.     case ESTABLISHED:
  632.         cb->state = CLI_OPEN_STATE;    /* shouldn't be needed */
  633.         break;
  634.     case CLOSE_WAIT:
  635.         close_tcp(tcb);            /* shut things down */
  636.         break;
  637.     case CLOSED:
  638.         /* if this close was not done by us ie. a RST */
  639.         if(cb->tfile != NULLFILE)
  640.             fclose(cb->tfile);
  641.         del_session(cb);
  642.         del_tcp(tcb);
  643.         break;
  644.     }
  645. }
  646.  
  647. /* Send message back to server */
  648. /*VARARGS*/
  649. static void
  650. sendit(cb,fmt,arg1,arg2)
  651. struct smtp_cb *cb;
  652. char *fmt,*arg1,*arg2;
  653. {
  654.     struct mbuf *bp;
  655.     char tmpstring[256];
  656.  
  657. #ifdef SMTPTRACE
  658.     if (smtptrace) {
  659.         printf(">>> ");
  660.         printf(fmt,arg1,arg2);
  661.         fflush(stdout);
  662.     }
  663. #endif
  664.     sprintf(tmpstring,fmt,arg1,arg2);
  665.     bp = qdata(tmpstring,(int16)strlen(tmpstring));
  666.     send_tcp(cb->tcb,bp);
  667. }
  668.  
  669. /* create mail lockfile */
  670. int
  671. mlock(dir,id)
  672. char *dir,*id;
  673. {
  674.     char lockname[LINELEN];
  675.     int fd;
  676.     /* Try to create the lock file in an atomic operation */
  677.     sprintf(lockname,"%s/%s.lck",dir,id);
  678.     if((fd = open(lockname, O_WRONLY|O_EXCL|O_CREAT,0600)) == -1)
  679.         return -1;
  680.     close(fd);
  681.     return 0;
  682. }
  683.  
  684. /* remove mail lockfile */
  685. int
  686. rmlock(dir,id)
  687. char *dir,*id;
  688. {
  689.     char lockname[LINELEN];
  690.     sprintf(lockname,"%s/%s.lck",dir,id);
  691.     return(unlink(lockname));
  692. }
  693.  
  694. /* free the message struct and data */
  695. static void
  696. del_session(cb)
  697. register struct smtp_cb *cb;
  698. {
  699.     register struct smtp_job *jp,*tp;
  700.     register int i;
  701.  
  702.     if (cb == NULLCB)
  703.         return;
  704.     for(i=0; i<MAXSESSIONS; i++) 
  705.         if(cli_session[i] == cb) {
  706.             cli_session[i] = NULLCB;
  707.             break;
  708.         }
  709.  
  710.     if(cb->wname != NULLCHAR)
  711.         free(cb->wname);
  712.     if(cb->tname != NULLCHAR)
  713.         free(cb->tname);
  714.     for (jp = cb->jobq; jp != NULLJOB;jp = tp) {
  715.             tp = jp->next;
  716.             del_job(jp);
  717.     }
  718.     del_list(cb->errlog);
  719.     free((char *)cb);
  720.     smtpsessions--;    /* number of connections active */
  721. }
  722.  
  723. void
  724. del_job(jp)
  725. register struct smtp_job *jp;
  726. {
  727.     if ( *jp->jobname != '\0')
  728.         (void) rmlock(mailqdir,jp->jobname);
  729.     if(jp->from != NULLCHAR)
  730.         free(jp->from);
  731.     del_list(jp->to);
  732.     free((char *)jp);
  733. }
  734.  
  735. /* delete a list of list structs */
  736. void
  737. del_list(lp)
  738. struct list *lp;
  739. {
  740.     register struct list *tp, *tp1;
  741.     for (tp = lp; tp != NULLLIST; tp = tp1) {
  742.             tp1 = tp->next;
  743.             if(tp->val != NULLCHAR)
  744.                 free(tp->val);
  745.             free((char *)tp);
  746.     }
  747. }
  748.  
  749. /* return message to sender */
  750. static void
  751. retmail(cb)
  752. struct smtp_cb *cb;
  753. {
  754.     register struct list *lp;
  755.     register FILE *tfile;
  756.     register int c;
  757.     FILE *infile,*tmpfile();
  758.     char *host,*to;
  759.     time_t t,time();
  760. #ifdef SMTPTRACE
  761.     if (smtptrace > 5) {
  762.         printf("smtp job %s returned to sender\n",cb->wname);
  763.         fflush(stdout);
  764.     }
  765. #endif
  766.     /* A null From<> so no looping replys to MAIL-DAEMONS */
  767.     to = cb->jobq->from;
  768.     if (*to == '\0')
  769.         return;
  770.     if ((host = index(to,'@')) == NULLCHAR)
  771.         host = hostname;
  772.     else
  773.         host++;
  774.     if ((infile = fopen(cb->tname,"r")) == NULLFILE)
  775.         return;
  776.     if ((tfile = tmpfile()) == NULLFILE) {
  777.         fclose(infile);
  778.         return;
  779.     }
  780.     time(&t);
  781.     fprintf(tfile,"Date: %s",ptime(&t));
  782.     fprintf(tfile,"Message-Id: <%ld@%s>\n",get_msgid(),hostname);
  783.     fprintf(tfile,"From: MAILER-DAEMON@%s\n",hostname);
  784.     fprintf(tfile,"To: %s\n",to);
  785.     fprintf(tfile,"Subject: Failed mail\n\n");
  786.     fprintf(tfile,"  ===== transcript follows =====\n\n");
  787.  
  788.     for (lp = cb->errlog; lp != NULLLIST; lp = lp->next)
  789.         fprintf(tfile,"%s\n",lp->val);
  790.  
  791.     fprintf(tfile,"\n  ===== Unsent message follows ====\n");
  792.  
  793.     while((c = getc(infile)) != EOF)
  794.         if (putc(c,tfile) == EOF)
  795.             break;
  796.     fclose(infile);
  797.     fseek(tfile,0L,0);
  798.     if ((smtpmode & QUEUE) != 0)
  799.         router_queue(cb->tcb,tfile,"",to);
  800.     else
  801.         queuejob(cb->tcb,tfile,host,to,"");
  802.     fclose(tfile);
  803. }
  804.  
  805. /* look to see if a smtp control block exists for this ipdest */
  806. static struct smtp_cb *
  807. lookup(destaddr)
  808. int32 destaddr;
  809. {
  810.     register int i;
  811.  
  812.     for(i=0; i<MAXSESSIONS; i++) {
  813.         if (cli_session[i] == NULLCB)
  814.             continue;
  815.         if(cli_session[i]->ipdest == destaddr)
  816.             return cli_session[i];
  817.     }
  818.     return NULLCB;
  819. }
  820.  
  821. /* create a new  smtp control block */
  822. static struct smtp_cb *
  823. newcb()
  824. {
  825.     register int i;
  826.     register struct smtp_cb *cb;
  827.  
  828.     for(i=0; i<MAXSESSIONS; i++) {
  829.         if(cli_session[i] == NULLCB) {
  830.             cb = (struct smtp_cb *)calloc(1,sizeof(struct smtp_cb));
  831.             if (cb == NULLCB)
  832.                 return(NULLCB);
  833.             cb->wname = malloc((unsigned)strlen(mailqdir)+JOBNAME);
  834.             if (cb->wname == NULLCHAR) {
  835.                 free((char *)cb);
  836.                 return(NULLCB);
  837.             }
  838.             cb->tname = malloc((unsigned)strlen(mailqdir)+JOBNAME);
  839.             if (cb->tname == NULLCHAR) {
  840.                 free(cb->wname);
  841.                 free((char *)cb);
  842.                 return(NULLCB);
  843.             }
  844.             cb->state = CLI_INIT_STATE;
  845.             cli_session[i] = cb;
  846.             smtpsessions++;    /* number of connections active */
  847.             return(cb);
  848.         }
  849.     }
  850.     return NULLCB;
  851. }
  852.  
  853. static void
  854. execjobs()
  855. {
  856.     struct socket lsocket, fsocket;
  857.     void smtp_rec(), smtp_cts(), smtp_state();
  858.     register struct smtp_cb *cb;
  859.     register int i;
  860.  
  861.     for(i=0; i<MAXSESSIONS; i++) {
  862.         cb = cli_session[i];
  863.         if (cb == NULLCB) 
  864.             continue;
  865.         if(cb->state != CLI_INIT_STATE)
  866.             continue;
  867.  
  868.         sprintf(cb->tname,"%s/%s.txt",mailqdir,cb->jobq->jobname);
  869.         sprintf(cb->wname,"%s/%s.wrk",mailqdir,cb->jobq->jobname);
  870.  
  871.         /* setup the socket */
  872.         fsocket.address = cb->ipdest;
  873.         fsocket.port = SMTP_PORT;
  874.         lsocket.address = ip_addr;    /* our ip address */
  875.         lsocket.port = lport++;        /* next unused port */
  876. #ifdef SMTPTRACE
  877.         if (smtptrace) {
  878.             printf("Trying Connection to %s\n",inet_ntoa(fsocket.address));
  879.             fflush(stdout);
  880.         }
  881. #endif
  882.  
  883.         /* open smtp connection */
  884.         cb->state = CLI_OPEN_STATE;    /* init state placeholder */
  885.         cb->tcb = open_tcp(&lsocket,&fsocket,TCP_ACTIVE,tcp_window,
  886.             smtp_rec,smtp_cts,smtp_state,0,(char *)cb);
  887.         cb->tcb->user = (char *)cb;    /* Upward pointer */
  888.     }
  889. }
  890.     
  891. /* add this job to control block queue */
  892. struct smtp_job *
  893. setupjob(cb,id,from)
  894. struct smtp_cb *cb;
  895. char *id,*from;
  896. {
  897.     register struct smtp_job *p1,*p2;
  898.  
  899.     p1 = (struct smtp_job *)calloc(1,sizeof(struct smtp_job));
  900.     if (p1 == NULLJOB)
  901.         return NULLJOB;
  902.     p1->from = malloc((unsigned)strlen(from) + 1);
  903.     if (p1->from == NULLCHAR) {
  904.         free((char *)p1);
  905.         return NULLJOB;
  906.     }
  907.     strcpy(p1->from,from);
  908.     strcpy(p1->jobname,id);
  909.     /* now add to end of jobq */
  910.     if ((p2 = cb->jobq) == NULLJOB)
  911.         cb->jobq = p1;
  912.     else {
  913.         while(p2->next != NULLJOB)
  914.             p2 = p2->next;
  915.         p2->next = p1;
  916.     }
  917.     return p1;
  918. }
  919.  
  920. /* called to advance to the next job */
  921. static int
  922. nextjob(cb)
  923. register struct smtp_cb *cb;
  924. {
  925.     register struct smtp_job *jp;
  926.  
  927.  
  928.     jp = cb->jobq->next;
  929.     del_job(cb->jobq);
  930.     if (jp == NULLJOB) {
  931.         cb->jobq = NULLJOB;
  932.         return 0;
  933.     }
  934.     /* remove the error log of previous message */
  935.     del_list(cb->errlog);
  936.     cb->errlog = NULLLIST;
  937.     cb->goodrcpt = 0;
  938.     cb->jobq = jp;
  939.     sprintf(cb->tname,"%s/%s.txt",mailqdir,jp->jobname);
  940.     sprintf(cb->wname,"%s/%s.wrk",mailqdir,jp->jobname);
  941. #ifdef SMTPTRACE
  942.     if (smtptrace > 5) {
  943.         printf("sending job %s\n",jp->jobname);
  944.         fflush(stdout);
  945.     }
  946. #endif
  947.         return 1;
  948.  
  949. }
  950.  
  951.  
  952. /* mail routing funtion. For now just used the hosts file */
  953. int32
  954. mailroute(dest)
  955. char *dest;
  956. {
  957.     int32 destaddr;
  958.  
  959.     /* look up address or use the gateway */
  960.     if ((destaddr = resolve(dest)) == 0)
  961.         if (gateway != 0) 
  962.             destaddr = gateway; /* Use the gateway  */
  963.     return destaddr;
  964.     
  965. }
  966.  
  967. /* save error reply for in error list */
  968. static void
  969. logerr(cb)
  970. struct smtp_cb *cb;
  971. {
  972.     register struct list *lp,*tp;
  973.     if ((tp = (struct list *)calloc(1,sizeof(struct list))) == NULLLIST)
  974.         return;
  975.     if ((tp->val = malloc((unsigned)strlen(cb->buf)+1)) == NULLCHAR) {
  976.         free((char *)tp);
  977.         return;
  978.     }
  979.     /* find end of list */
  980.     if ((lp = cb->errlog) == NULLLIST)
  981.         cb->errlog = tp;
  982.     else {
  983.         while(lp->next != NULLLIST)
  984.             lp = lp->next;
  985.         lp->next = tp;
  986.     }
  987.     strcpy(tp->val,cb->buf);
  988. }
  989.